﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ServiceStack.Redis;
using ServiceStack.Redis.Support;

namespace Epona.AOP.Cache.Impl
{
    public class RedisCacheRepository : ICacheRepository
    {
        ObjectSerializer ser;

        public RedisCacheRepository()
        {
            ser = new ObjectSerializer();
        }

        public bool Insert(string key, object o, TimeSpan? expire = null)
        {
            Func<IRedisClient, bool> fun = (IRedisClient client) =>
                {
                    if (expire != null)
                    {
                        return client.Set<byte[]>(key, ser.Serialize(o), expire.Value);
                    }

                    return client.Set<byte[]>(key, ser.Serialize(o));
                };

            return Write(fun);
        }

        public bool Insert<T>(string key, T t, TimeSpan? expire = null)
        {
            Func<IRedisClient, bool> fun = (IRedisClient client) =>
                {
                    if (expire != null)
                    {
                        return client.Set<T>(key, t, expire.Value);
                    }

                    return client.Set<T>(key, t);
                };

            return Write(fun);
        }

        public bool Delete(string key)
        {
            Func<IRedisClient, bool> fun = (IRedisClient client) =>
                {
                    return client.Remove(key);
                };

            return Write(fun);
        }

        public bool Update(string key, object o, TimeSpan? expire)
        {
            Func<IRedisClient, bool> fun = (IRedisClient client) =>
                {
                    if (client.ContainsKey(key))
                        client.Remove(key);

                    if (expire != null)
                        return client.Set<byte[]>(key, ser.Serialize(o), expire.Value);
                    return client.Set<byte[]>(key, ser.Serialize(o));
                };

            return Write(fun);
        }

        public bool Update<T>(string key, T t, TimeSpan? expire)
        {
            Func<IRedisClient, bool> fun = (IRedisClient client) =>
            {
                if (client.ContainsKey(key))
                    client.Remove(key);

                if (expire != null)
                    return client.Set<T>(key, t, expire.Value);

                return client.Set<T>(key, t);
            };

            return Write(fun);
        }

        public bool ReSetExpire(string key, TimeSpan expire)
        {
            Func<IRedisClient, bool> fun = (IRedisClient client) =>
                {
                    string ser = client.GetValue(key.ToString());
                    if (ser != null)
                    {
                        client.ExpireEntryIn(key, expire);
                        return true;
                    }
                    return false;
                };

            return Write(fun);
        }

        public IQueryable<object> GetAll()
        {
            throw new NotImplementedException();
        }

        public IList<string> GetKeys(string pattern)
        {
            Func<IRedisClient, object> fun = (IRedisClient client) =>
            {
                return client.SearchKeys(pattern);
            };

            return (List<string>)Read(fun);
        }

        public object Get(string key)
        {
            Func<IRedisClient, object> fun = (IRedisClient client) =>
                {
                    return ser.Deserialize(client.Get<byte[]>(key));
                };

            return Read(fun);
        }

        public T Get<T>(string key) where T : class
        {
            Func<IRedisClient, T> fun = (IRedisClient client) =>
                {
                    return client.Get<T>(key);
                };
            return (T)Read(fun);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="keys"></param>
        /// <returns></returns>
        public IDictionary<string, T> GetList<T>(ICollection<string> keys)
        {
            Func<IRedisClient, IDictionary<string, T>> fun = (IRedisClient client) =>
                {
                    //var existKeys = new List<string>();
                    //foreach (string key in keys)
                    //{
                    //    if (client.ContainsKey(key))
                    //        existKeys.Add(key);
                    //}
                    return client.GetAll<T>(keys);
                };
            return (IDictionary<string, T>)Read(fun);
        }

        public bool IsExist(string key)
        {
            Func<IRedisClient, object> fun = (IRedisClient client) =>
            {
                string ser = "";
                ser = client.GetValue(key.ToString());

                return string.IsNullOrEmpty(ser) == false;
            };

            return (bool)Read(fun);
        }

        public TimeSpan GetTimeToLive(string key)
        {
            Func<IRedisClient, object> fun = (IRedisClient client) =>
                {
                    return client.GetTimeToLive(key);
                };

            return (TimeSpan)Read(fun);
        }

        public void FlushAll()
        {
            IRedisClient client = null;
            try
            {
                using (client = RedisCacheManager.RedisCacheManagerSingleton().Manager.GetClient())
                {
                    client.FlushAll();
                }
            }
            catch
            {
            }
            finally
            {
                if (client != null)
                {
                    client.Dispose();
                }
            }
        }

        private object Read(Func<IRedisClient, object> doRead)
        {
            IRedisClient client = null;
            try
            {
                using (client = RedisCacheManager.RedisCacheManagerSingleton().Manager.GetClient())
                {
                    return doRead(client);
                }
            }
            catch
            {
                return null;
            }
            finally
            {
                if (client != null)
                {
                    client.Dispose();
                }
            }
        }

        private bool Write(Func<IRedisClient, bool> doWrite)
        {
            IRedisClient client = null;
            try
            {
                using (client = RedisCacheManager.RedisCacheManagerSingleton().Manager.GetClient())
                {
                    return doWrite(client);
                }
            }
            catch (RedisException)
            {
                throw new Exception("Redis写入异常.Host:" + client.Host + ",Port:" + client.Port);
            }
            finally
            {
                if (client != null)
                {
                    client.Dispose();
                }
            }
        }
    }
}
